#pragma once

#include <memory>
// #include "GDX11Renderer/GDXBuffer.h"
// #include "Graphics/Geometry.h"
// 
// #include "GDX11Renderer/Effect/Effects.h"
// #include "GUI/GUI.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/array.hpp>
#include "GRendererInfra/GRiMesh.h"
#include <GRendererInfra/GRiSceneObject.h>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#include <GGenericInfra/GGiEngineUtil.h>
#include <fstream>
#include <GRendererInfra\GRiObj.h>
#include <GRendererInfra\GRiObj.h>
/*using namespace std;*/

 
struct GMaterial
{
	float KD[3] = { 0.0f, 0.0f, 0.0f };

private:

	friend class boost::serialization::access;

	template<class Archive>
	void serialize(Archive& ar, const unsigned int version)
	{
		ar& BOOST_SERIALIZATION_NVP(KD);
	}
};

struct GObjectInfo
{
	std::string UniqueName = "none";

	GMaterial Material;

	std::string MeshUniqueName = "none";

// 	bool isLight;


	float Location[3] = { 0.0f, 0.0f, 0.0f };
	float Rotation[3] = { 0.0f, 0.0f, 0.0f };
	float Scale[3] = { 1.0f, 1.0f, 1.0f };

private:

	friend class boost::serialization::access;

	template<class Archive>
	void serialize(Archive& ar, const unsigned int version)
	{
		ar& BOOST_SERIALIZATION_NVP(UniqueName);
		ar& BOOST_SERIALIZATION_NVP(Material);
		ar& BOOST_SERIALIZATION_NVP(MeshUniqueName);
		ar& BOOST_SERIALIZATION_NVP(Location);
		ar& BOOST_SERIALIZATION_NVP(Rotation);
		ar& BOOST_SERIALIZATION_NVP(Scale);
	}
};
struct GMeshInfo
{
	std::string MeshUniqueName;

	friend class boost::serialization::access;

	template<class Archive>
	void serialize(Archive& ar, const unsigned int version)
	{
		ar& BOOST_SERIALIZATION_NVP(MeshUniqueName);
	}
};

class GScene
{

public:
	std::string mSkyCubemapUniqueName;
	std::vector<GObjectInfo> mSceneObjectInfo;
	std::vector<GMeshInfo> mMeshInfo;

	GScene() {};
	~GScene() {};

	
	
	void Serialize(std::string filename)
	{
		std::ifstream ifs;
		ifs.open(filename);
		if (ifs.good()) {
			{
				try
				{
					//boost::archive::binary_wiarchive ia(ifs);
					boost::archive::xml_iarchive ia(ifs);
					//boost::archive::text_iarchive ia(ifs);
					try
					{
						ia >> boost::serialization::make_nvp("Project", *this);
					}
					catch (boost::archive::archive_exception const& e)
					{
						std::string eMessage(e.what());
						auto wErrorMessage = L"Fail to load project file.\n" + GGiEngineUtil::StringToWString(eMessage);
						MessageBox(nullptr, wErrorMessage.c_str(), L"Other Exception", MB_OK);
						ifs.close();
						return;
					}
				}
				catch (std::exception & e)
				{
					std::string eMessage(e.what());
					auto wErrorMessage = L"Fail to create archive. Project file may be empty or out of date.\n" + GGiEngineUtil::StringToWString(eMessage);
					MessageBox(nullptr, wErrorMessage.c_str(), L"Other Exception", MB_OK);
					ifs.close();
					return;
				}
			}
			ifs.close();
		}
	}

	
	void SaveProject(
		std::string filename,
		std::string skyCubemapUniqueName,
		std::vector<GRiObj*>& pSceneObjects,
		std::unordered_map<std::string, std::unique_ptr<GRiMesh>>& pMeshes
	)
	{
		mSkyCubemapUniqueName = skyCubemapUniqueName;

	
		mSceneObjectInfo.clear();

		for (auto i = 0u; i < pSceneObjects.size(); i++)
		{
			if (pSceneObjects[i]->HasComponent<GRiCmpLight>())
			{
				continue;
			}
			GObjectInfo soInfo;
			soInfo.UniqueName = pSceneObjects[i]->UniqueName;
			soInfo.MeshUniqueName = pSceneObjects[i]->GetComponent<GRiCmpGeometry>()->GetMesh()->UniqueName;
			//soInfo.MaterialUniqueName = pSceneObjects[i]->GetMaterial()->UniqueName;
			/*soInfo.OverrideMaterialUniqueName.clear();*/
// 			auto ovrdMatNames = pSceneObjects[i]->GetOverrideMaterialNames();
// 			for (auto pair : ovrdMatNames)
// 			{
// 				GStrPair pr;
// 				pr.str1 = pair.first;
// 				pr.str2 = pair.second;
// 				soInfo.OverrideMaterialUniqueName.push_back(pr);
// 			}
			auto loc = pSceneObjects[i]->GetComponent<GRiCmpTransform>()->GetLocation();
			soInfo.Location[0] = loc[0];
			soInfo.Location[1] = loc[1];
			soInfo.Location[2] = loc[2];
			auto rot = pSceneObjects[i]->GetComponent<GRiCmpTransform>()->GetRotation();
			soInfo.Rotation[0] = rot[0];
			soInfo.Rotation[1] = rot[1];
			soInfo.Rotation[2] = rot[2];
			auto scale = pSceneObjects[i]->GetComponent<GRiCmpTransform>()->GetScale();
			soInfo.Scale[0] = scale[0];
			soInfo.Scale[1] = scale[1];
			soInfo.Scale[2] = scale[2];

			auto mat = pSceneObjects[i]->GetComponent<GRiCmpMaterial>()->Mat;

			soInfo.Material.KD[0] = mat.kd[0];
			soInfo.Material.KD[1] = mat.kd[1];
			soInfo.Material.KD[2] = mat.kd[2];

			mSceneObjectInfo.push_back(soInfo);
		}

		mMeshInfo.clear();

		for (auto it = pMeshes.begin(); it != pMeshes.end(); it++)
		{
			GMeshInfo mInfo;
			mInfo.MeshUniqueName = (*it).second->UniqueName;
//			mInfo.MaterialUniqueName.clear();
// 			for (auto& submesh : (*it).second->Submeshes)
// 			{
// 				GStrPair pr;
// 				pr.str1 = submesh.first;
// 				pr.str2 = submesh.second.GetMaterial()->UniqueName;
// 				mInfo.MaterialUniqueName.push_back(pr);
// 
// 				//mInfo.MaterialUniqueName[submesh.first] = submesh.second.GetMaterial()->UniqueName;
// 			}

			mMeshInfo.push_back(mInfo);
		}

		std::ofstream ofs;
		//ofs.open(filename, std::ios_base::out | std::ios_base::binary);
		ofs.open(filename);
		if (ofs.good())
		{
			{
				//boost::archive::binary_woarchive oa(ofs);
				boost::archive::xml_oarchive oa(ofs);
				//boost::archive::text_oarchive oa(ofs);

				oa << boost::serialization::make_nvp("Project", *this);
			}
			ofs.close();
		}

	}
private:

	friend class boost::serialization::access;

	template<class Archive>
	void serialize(Archive& ar, const unsigned int version)
	{
		ar& BOOST_SERIALIZATION_NVP(mSkyCubemapUniqueName);
		ar& BOOST_SERIALIZATION_NVP(mSceneObjectInfo);
		ar& BOOST_SERIALIZATION_NVP(mMeshInfo);
	}

};

